package org.jsola.hr.service.impl;

import org.apache.commons.jexl2.Expression;
import org.apache.commons.jexl2.JexlContext;
import org.apache.commons.jexl2.JexlEngine;
import org.apache.commons.jexl2.MapContext;
import org.apache.commons.lang.StringUtils;
import org.jsola.core.Page;
import org.jsola.hr.common.ExampleKit;
import org.jsola.hr.constant.HrConstants;
import org.jsola.hr.dao.ISalaryPlanDAO;
import org.jsola.hr.dto.EmployeeForSalaryDTO;
import org.jsola.hr.dto.SalaryPlanAddDTO;
import org.jsola.hr.dto.SalaryPlanFormulaAddDTO;
import org.jsola.hr.dto.SalaryPlanUpdateDTO;
import org.jsola.hr.entity.SalaryPlanDO;
import org.jsola.hr.query.SalaryPlanQuery;
import org.jsola.hr.service.ISalaryPlanFormulaService;
import org.jsola.hr.service.ISalaryPlanService;
import org.jsola.hr.vo.*;
import org.jsola.user.core.TokenUser;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import tk.mybatis.mapper.entity.Example;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

/**
 * @author zhr
 */
@Service("hrSalaryPlanServiceImpl")
public class SalaryPlanServiceImpl implements ISalaryPlanService {

    @Autowired
    private ISalaryPlanDAO salaryPlanDAO;
    @Autowired
    private ISalaryPlanFormulaService salaryPlanFormulaService;

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SalaryPlanVO save(SalaryPlanAddDTO salaryPlanAddDTO, TokenUser tokenUser) {
        // 转DO
        SalaryPlanDO salaryPlanDO = salaryPlanAddDTO.to(SalaryPlanDO.class);
        // 保存
        salaryPlanDO = save(salaryPlanDO, tokenUser.getSiteId(), tokenUser.getUserId());
        return salaryPlanDO.to(SalaryPlanVO.class);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public SalaryPlanVO add(SalaryPlanAddDTO salaryPlanAddDTO, TokenUser tokenUser) {
       SalaryPlanVO salaryPlanVO= this.save(salaryPlanAddDTO,tokenUser);
       List<SalaryPlanFormulaAddDTO> list=  salaryPlanAddDTO.getSalaryPlanFormulaAddDTOList();
        salaryPlanFormulaService.addList(salaryPlanVO.getId(),list,tokenUser);
        return salaryPlanVO;
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public List<SalaryPlanVO> batchSave(List<SalaryPlanAddDTO> salaryPlanAddDTOList, TokenUser tokenUser) {
        if(CollectionUtils.isEmpty(salaryPlanAddDTOList)) {
            return null;
        }
        List<SalaryPlanDO> salaryPlanDOList = salaryPlanAddDTOList
                .parallelStream()
                .map(salaryPlanAddDTO -> {
                    SalaryPlanDO salaryPlanDO = salaryPlanAddDTO.to(SalaryPlanDO.class);
                    salaryPlanDO.preInsert(tokenUser.getUserId());
                    salaryPlanDO.setSiteId(tokenUser.getSiteId());
                    return salaryPlanDO;
                }).collect(Collectors.toList());
        salaryPlanDAO.insertListAndSetId(salaryPlanDOList);
        return  salaryPlanDOList
                .parallelStream()
                .map(salaryPlanDO -> salaryPlanDO.to(SalaryPlanVO.class))
                .collect(Collectors.toList());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int update(SalaryPlanUpdateDTO salaryPlanUpdateDTO, TokenUser tokenUser) {
        //转DO
        SalaryPlanDO salaryPlanDO = salaryPlanUpdateDTO.to(SalaryPlanDO.class);
        //根据主键更新，只更新非null值
        return updateByIdSelective(salaryPlanDO, tokenUser.getSiteId(), tokenUser.getUserId());
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int updateForBusiness(SalaryPlanUpdateDTO salaryPlanUpdateDTO, TokenUser tokenUser) {
        salaryPlanFormulaService.updateForBusiness(salaryPlanUpdateDTO.getId(),salaryPlanUpdateDTO.getSalaryPlanFormulaAddDTOList(),tokenUser);
        return  this.update(salaryPlanUpdateDTO,tokenUser);
    }

    @Override
    @Transactional(value = HrConstants.TX, rollbackFor = Exception.class)
    public int deleteByIds(TokenUser tokenUser, String... salaryPlanIds) {
        return logicalDeleteByIds(tokenUser.getSiteId(), tokenUser.getUserId(), (Object[]) salaryPlanIds);
    }


    @Override
    public SalaryPlanVO selectById(String salaryPlanId, String siteId) {
        return   salaryPlanDAO.selectSalaryInfoByPrimaryKey(salaryPlanId,siteId);
    }

    @Override
    public List<SalaryPlanListVO> select(SalaryPlanQuery salaryPlanQuery, String siteId) {
        List<SalaryPlanDO> salaryPlanDOList = selectDO(salaryPlanQuery, siteId);
        if(CollectionUtils.isEmpty(salaryPlanDOList)) {
            return salaryPlanDOList == null ? null : new ArrayList<>();
        }
        return salaryPlanDOList.stream()
                .map(salaryPlanDO -> salaryPlanDO.to(SalaryPlanListVO.class))
                .collect(Collectors.toList());
    }

    @Override
    public int selectCount(SalaryPlanQuery salaryPlanQuery, String siteId) {
        Example example = buildExample(salaryPlanQuery,siteId);
        return salaryPlanDAO.selectCountByExample(example);
    }

    @Override
    public org.jsola.core.Page<SalaryPlanListVO> selectPage(SalaryPlanQuery salaryPlanQuery, String siteId) {
        Example example = buildExample(salaryPlanQuery,siteId);
        Page<SalaryPlanDO> page = salaryPlanDAO.selectPageByExample(example,
                salaryPlanQuery.getPageNo(),
                salaryPlanQuery.getPageSize());

        return page.to(SalaryPlanListVO.class);
    }

    @Override
    public SalaryPlanDO selectDOById(String salaryPlanId, String siteId) {
        return listById(salaryPlanId, siteId);
    }

    @Override
    public List<SalaryPlanDO> selectDO(SalaryPlanQuery salaryPlanQuery, String siteId) {
        Example example = buildExample(salaryPlanQuery,siteId);
        return salaryPlanDAO.selectByExample(example);
    }

    @Override
    public List<EmployeeForSalaryDTO>  calculateEmployeeSalary(List<EmployeeForSalaryDTO> employeeList, String siteId) throws IllegalAccessException {
            List<SalaryPlanVO> salaryPlanVOList=   salaryPlanDAO.selectSalaryInfo(siteId);
            for (EmployeeForSalaryDTO employee:employeeList){
                Map<String, Object> params= employee.getParams();
                    for(SalaryPlanVO salaryPlanVO:salaryPlanVOList){
                        SalaryPlanFormulaVO planFormulaVO=   this.getValidPlanFormula(salaryPlanVO,params);
                        if (planFormulaVO!=null){
                          String formula=  planFormulaVO.getFormula();
                          Object result=  this.calculate(formula,params);
                          salaryPlanVO.setSalaryAmount((Integer) result);
                        }
                    }
                    employee.setSalaryPlanVOList(salaryPlanVOList);
            }

            return employeeList;
    }

    /**
     * 获取当前员工应调用哪个公式
     * @param salaryPlanVO  方案
     * @param params 员工的属性
     * @return   应使用方案
     */
   private   SalaryPlanFormulaVO getValidPlanFormula( SalaryPlanVO salaryPlanVO,Map<String, Object> params){
        SalaryPlanFormulaVO validPlanFormula = null;
        List<SalaryPlanFormulaVO> planFormulaVOList = salaryPlanVO.getPlanFormulaVOList();
        if (planFormulaVOList==null || planFormulaVOList.size()<1){
            return  validPlanFormula;
        }

        for (SalaryPlanFormulaVO planFormulaVO : planFormulaVOList) {
            Integer relation = planFormulaVO.getConditionRelation();
            List<SalaryFormulaConditionVO> conditionVOList = planFormulaVO.getFormulaConditionVOList();
            List<String> expressionList = new ArrayList<>();
            if (conditionVOList==null || conditionVOList.size()<1){
                continue;
            }
            for (SalaryFormulaConditionVO conditionVO : conditionVOList) {
                String exp = conditionVO.getPropertyName() + conditionVO.getComparisonOperator() + conditionVO.getCondition();
                expressionList.add(exp);
            }
            String expression = StringUtils.join(expressionList, relation == 1 ? "&&" : "||");
            Object result = this.calculate(expression, params);
            if (result instanceof Boolean) {
                if ((Boolean) result != null && (Boolean) result && (validPlanFormula == null || planFormulaVO.getSort() > validPlanFormula.getSort())) {
                    validPlanFormula = planFormulaVO;
                }
            }
        }
        return validPlanFormula;
    }



    @Override
    public Object calculate(String formula, Map<String, Object> params) {
        JexlContext jexlContext=new MapContext();
        params.forEach((key,value)-> jexlContext.set(key,String.valueOf(value)));
        Expression expression=new JexlEngine().createExpression(formula);
        return expression.evaluate(jexlContext);
    }

    /**
     * 根据查询参数，构建example

     * @param salaryPlanQuery 查询参数
     * @param siteId 所属站点id
     * @return example
     */
    private Example buildExample(SalaryPlanQuery salaryPlanQuery, String siteId) {
        Example example = new Example(SalaryPlanDO.class);
        example.and()
                .andEqualTo("valid", true)
                .andEqualTo("siteId", siteId);
        if (salaryPlanQuery.getName() != null) {
            example.and().andLike("name", "%" + salaryPlanQuery.getName() + "%");
        }
        if (salaryPlanQuery.getType() != null) {
            example.and().andEqualTo("type", salaryPlanQuery.getType());
        }
        if (salaryPlanQuery.getBeforeTax() != null) {
            example.and().andEqualTo("beforeTax", salaryPlanQuery.getBeforeTax());
        }
        if (salaryPlanQuery.getNextMonthClear() != null) {
            example.and().andEqualTo("nextMonthClear", salaryPlanQuery.getNextMonthClear());
        }
        if (salaryPlanQuery.getSystemProperty() != null) {
            example.and().andEqualTo("systemProperty", salaryPlanQuery.getSystemProperty());
        }
        if (salaryPlanQuery.getRemark() != null) {
            example.and().andEqualTo("remark", salaryPlanQuery.getRemark());
        }
        if (salaryPlanQuery.getEnable() != null) {
            example.and().andEqualTo("enable", salaryPlanQuery.getEnable());
        }
        if (salaryPlanQuery.getCompanyId() != null) {
            example.and().andEqualTo("companyId", salaryPlanQuery.getCompanyId());
        }
        if (salaryPlanQuery.getGmtCreateStart() != null &&
                salaryPlanQuery.getGmtCreateEnd() != null) {
            example.and().andBetween("gmtCreate", salaryPlanQuery.getGmtCreateStart(),
                    salaryPlanQuery.getGmtCreateEnd());
        }
        if (salaryPlanQuery.getCreateUserId() != null) {
            example.and().andLike("createUserId", "%" + salaryPlanQuery.getCreateUserId() + "%");
        }
        if (salaryPlanQuery.getGmtModifiedStart() != null &&
                salaryPlanQuery.getGmtModifiedEnd() != null) {
            example.and().andBetween("gmtModified", salaryPlanQuery.getGmtModifiedStart(),
                    salaryPlanQuery.getGmtModifiedEnd());
        }
        if (salaryPlanQuery.getModifiedUserId() != null) {
            example.and().andLike("modifiedUserId", "%" + salaryPlanQuery.getModifiedUserId() + "%");
        }
        // 排序
        ExampleKit.setExampleOrder(example,salaryPlanQuery.getOrders());
        return example;
    }
}




